DispatcherServlet의 동작 과정

@Wonyu· February 22, 2025 · 4 min read

Spring MVC에서 프론트 컨트롤러 역할을 하는 핵심 서블릿
모든 HTTP 요청을 받아 적절한 컨트롤러로 전달하고, 응답을 생성하여 클라이언트에게 반환

📌 Front Controller 패턴

모든 요청을 한 곳에서 처리하고, 이후 적절한 컨트롤러로 위임하는 패턴

만약 DispatcherServlet이 없다면, Servlet 컨테이너가 요청에 맞는 핸들러를 찾아야하며, 모든 요청마다 개별 서블릿을 사용해야 한다. 따라서 모든 요청을 DispatcherServlet이 처리하도록 하는 프론트 컨트롤러 패턴이 적용된다.

장점

  • 모든 요청을 프론트 컨트롤러에서 관리하여 중복을 줄일 수 있음
  • 컨트롤러 별로 로직을 분리하면서도, 공통 작업(예: 보안, 로깅)을 프론트 컨트롤러에서 처리 가능

📌 요청 처리 과정

dispatcherServlet

  1. DispatcherServlet에 요청이 전달
  2. DispatcherServlet이 HandlerMapping를 활용해 적절한 핸들러를 조회
  3. DispatcherServlet이 HandlerAdapter를 활용해 해당 핸들러를 실행
  4. 핸들러가 작성된 비즈니스 로직대로 요청을 처리
  5. 핸들러가 반환값 반환
  6. 핸들러 어댑터가 반환값 처리
  7. DispatcherServlet이 응답 반환

HandlerMapping

요청을 분석하여 어떤 컨트롤러가 처리할지 결정
적절한 컨트롤러를 찾아 반환

HandlerMapping.class

public interface HandlerMapping {  
    @Nullable  
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;  
}

🔹 getHandler(request)
요청을 분석해 적절한 HandlerExecutionChain를 반환

🔹 HandlerExecutionChain
HandlerMapping이 찾은 핸들러(컨트롤러)와 해당 핸들러에 적용할 HandlerInterceptor(인터셉터)들을 함께 관리하는 객체

DispatcherServlet.class

protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {  
    if (this.handlerMappings != null) {  
        Iterator var2 = this.handlerMappings.iterator();  
  
        while(var2.hasNext()) {  
            HandlerMapping mapping = (HandlerMapping)var2.next();  
            HandlerExecutionChain handler = mapping.getHandler(request);  
            if (handler != null) {  
                return handler;  
            }  
        }  
    }  
  
    return null;  
}

HandlerAdapter

HandlerMapping이 찾아준 컨트롤러를 실행하는 역할

HandlerAdapter.class

public interface HandlerAdapter {  
    boolean supports(Object handler);  
  
    @Nullable  
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;   
}

🔹 supports(handler)
해당 핸들러(컨트롤러)가 이 HandlerAdapter에서 실행할 수 있는지 확인하는 메서드

🔹 handle(request, response, handler)
HandlerMapping이 반환한 컨트롤러를 실행하는 메서드

DispatcherServlet.class

protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {  
    if (this.handlerAdapters != null) {  
        Iterator var2 = this.handlerAdapters.iterator();  
  
        while(var2.hasNext()) {  
            HandlerAdapter adapter = (HandlerAdapter)var2.next();  
            if (adapter.supports(handler)) {  
                return adapter;  
            }  
        }  
    }  
}

📌 궁금했던 점

어떻게 다양한 타입의 응답 데이터를 처리할까?

RequestMappingHandlerAdapter.class 핸들러의 반환 응답 형식에 따라 분기하여 반환값을 처리한다.

private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {  
    List<HandlerMethodReturnValueHandler> handlers = new ArrayList(20);  
    handlers.add(new ModelAndViewMethodReturnValueHandler());  
    handlers.add(new RequestResponseBodyMethodProcessor(this.getMessageConverters(), this.contentNegotiationManager, this.requestResponseBodyAdvice));
    ...
    return handlers;
    }

DispatcherServlet이 다양한 응답 형식을 어떻게 추상화해서 반환하는가?

DispatcherServlet은 실제로는 응답을 반환하는 것이 아닌 HttpServletResponse에 응답 데이터가 기록되도록 한다. 따라서 다양한 HandlerMethodReturnValueHandler가 HttpServletResponse에 응답 데이터를 기록한다.

DispatcherServlet.class

protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
    HandlerExecutionChain mappedHandler = getHandler(request);
    if (mappedHandler == null) {
        noHandlerFound(request, response);
        return;
    }

    HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
    
    // 🚀 컨트롤러 실행
    ModelAndView mv = ha.handle(request, response, mappedHandler.getHandler());

    // 🚀 반환된 결과를 처리
    processDispatchResult(request, response, mappedHandler, mv, null);
}

📌 참고

https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-servlet.html https://mangkyu.tistory.com/18

@Wonyu
Hello :) I'm Wonyu